#include "stdafx.h"
#include "IFourierTransform.h"
#include "FourierTransformFactoryCuFft.h"
#include "FourierTransformFactoryIppFft.h"
#include "FourierTransformFactoryFFTW.h"
#include "FourierTransformFactoryD3DCSX.h"
#include "FourierTransformFactoryclFFT.h"
#include "PerformanceDataStore.h"
#include "delayloadUtils.h"

using namespace FTLib;

bool FTLib::InitializeFTLib(std::function<const wchar_t*(void)> funcEnumAdditionalDllsFolders)
{
	if (funcEnumAdditionalDllsFolders)
	{
		InitializeDelayLoadAdditionalDllFoldersHook(funcEnumAdditionalDllsFolders);
	}

	return true;
}

void FTLib::ShutdownFTLib()
{
	if (CFourierTransformFactoryClFFTFft::HasBeenCheckedAndIsAvailable())
	{
		CFourierTransformFactoryClFFTFft::Shutdown();
	}
}

std::vector<FourierTransformEngines> FTLib::GetListOfEngines()
{
	return std::vector<FourierTransformEngines>(
		{ 
			FourierTransformEngines::CuFft, 
			FourierTransformEngines::Ipp,
			FourierTransformEngines::FFTW,
			FourierTransformEngines::D3DCSX,
			FourierTransformEngines::CLFFT
		});
}

std::shared_ptr<IFourierTransformFactory> FTLib::CreateFourierTransformEngine(FourierTransformEngines type, const wchar_t* initializationArguments/* = nullptr*/)
{
	switch (type)
	{
	case FourierTransformEngines::CuFft:
		if (IsEngineAvailable(FourierTransformEngines::CuFft))
		{
			return std::make_shared<CFourierTransformFactoryCuFft>(initializationArguments != nullptr ? std::wstring(initializationArguments) : std::wstring());
		}
		else
		{
			return std::shared_ptr<IFourierTransformFactory>();
		}
	case FourierTransformEngines::Ipp:
		if (IsEngineAvailable(FourierTransformEngines::Ipp))
		{
			return std::make_shared<CFourierTransformFactoryIppFft>();
		}
		else
		{
			return std::shared_ptr<IFourierTransformFactory>();
		}
	case FourierTransformEngines::FFTW:
		if (IsEngineAvailable(FourierTransformEngines::FFTW))
		{
			return std::make_shared<CFourierTransformFactoryFFTWFft>();
		}
		else
		{
			return std::shared_ptr<IFourierTransformFactory>();
		}
	case FourierTransformEngines::D3DCSX:
		if (IsEngineAvailable(FourierTransformEngines::D3DCSX))
		{
			return std::make_shared<CFourierTransformFactoryD3DCSXFft>();
		}
		else
		{
			return std::shared_ptr<IFourierTransformFactory>();
		}
	case FourierTransformEngines::CLFFT:
		if (IsEngineAvailable(FourierTransformEngines::CLFFT))
		{
			return std::make_shared<CFourierTransformFactoryClFFTFft>(initializationArguments != nullptr ? std::wstring(initializationArguments) : std::wstring());
		}
		else
		{
			return std::shared_ptr<IFourierTransformFactory>();
		}
	}

	return std::shared_ptr<IFourierTransformFactory>();
}

bool FTLib::IsEngineAvailable(FourierTransformEngines type)
{
	switch (type)
	{
	case FTLib::FourierTransformEngines::CuFft:
		return CFourierTransformFactoryCuFft::GetIsAvailable();
	case FTLib::FourierTransformEngines::Ipp:
		return CFourierTransformFactoryIppFft::GetIsAvailable();
	case FTLib::FourierTransformEngines::FFTW:
		return CFourierTransformFactoryFFTWFft::GetIsAvailable();
	case FTLib::FourierTransformEngines::D3DCSX:
		return CFourierTransformFactoryD3DCSXFft::GetIsAvailable();
	case FTLib::FourierTransformEngines::CLFFT:
		return CFourierTransformFactoryClFFTFft::GetIsAvailable();
	default:
		break;
	}

	return true;
}

static void GetEngineInfo_Name(FourierTransformEngines type, std::wstring& info)
{
	switch (type)
	{
	case FTLib::FourierTransformEngines::CuFft:
		info = L"CuFFT";
		break;
	case FTLib::FourierTransformEngines::Ipp:
		info = L"IPP";
		break;
	case FTLib::FourierTransformEngines::FFTW:
		info = L"FFTW";
		break;
	case FTLib::FourierTransformEngines::D3DCSX:
		info = L"D3DCSX";
		break;
	case FTLib::FourierTransformEngines::CLFFT:
		info = L"clFFT";
		break;
	default:
		throw std::invalid_argument("Unrecognized FT-engine-type.");
	}
}

static void GetEngineInfo_Version(FourierTransformEngines type, std::wstring& info)
{
	switch (type)
	{
	case FTLib::FourierTransformEngines::Ipp:
		CFourierTransformFactoryIppFft::GetVersionString(info);
		break;
	case FTLib::FourierTransformEngines::CuFft:
		CFourierTransformFactoryCuFft::GetVersionString(info);
		break;
	case FTLib::FourierTransformEngines::FFTW:
		CFourierTransformFactoryFFTWFft::GetVersionString(info);
		break;
	case FTLib::FourierTransformEngines::D3DCSX:
		CFourierTransformFactoryD3DCSXFft::GetVersionString(info);
		break;
	case FTLib::FourierTransformEngines::CLFFT:
		CFourierTransformFactoryClFFTFft::GetVersionString(info);
		break;
	default:
		throw std::invalid_argument("Unrecognized FT-engine-type.");
	}
}

void FTLib::GetEngineInfo(EngineInfoType infoType, FourierTransformEngines type, std::wstring& info)
{
	switch (infoType)
	{
	case EngineInfoType::Name:
		GetEngineInfo_Name(type, info);
		break;
	case EngineInfoType::Version:
		GetEngineInfo_Version(type, info);
		break;
	}
}

std::shared_ptr<IPerformanceData> FTLib::CreatePerformanceData()
{
	return std::make_shared<CPerformanceDataStore>();
}
